﻿using System;
using System.Collections.Generic;
using System.Reflection;
using MugenInjection.Bindings.Builders;
using MugenInjection.Delegates;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;
using MugenInjection.Scope;

namespace MugenInjection.Bindings
{
    /// <summary>
    ///     Represents the type binding.
    /// </summary>
    public class TypeBinding : BindingBase
    {
        #region Fields

        private readonly IActivator _activator;
        private readonly IConstructorResolver _constructorResolver;
        private readonly bool _isGenericTypeDefinition;
        private readonly Type _typeTo;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="TypeBinding" /> class.
        /// </summary>
        /// <param name="typeTo">The specified type for activate.</param>
        /// <param name="constructorResolver">
        ///     The specified <see cref="IConstructorResolver" />.
        /// </param>
        /// <param name="services">The specified services types.</param>
        /// <param name="scopeLifecycle">
        ///     The specified <see cref="IScopeLifecycle" />.
        /// </param>
        /// <param name="parameters">The specified parameters.</param>
        /// <param name="canResolve">The specified condition for can resolve method.</param>
        /// <param name="activator">
        ///     The specified <see cref="IActivator" />.
        /// </param>
        /// <param name="isSelfBindable">Indicates that binding was build as self bindable.</param>
        public TypeBinding(Type typeTo, IActivator activator, IConstructorResolver constructorResolver,
                           IList<Type> services,
                           IScopeLifecycle scopeLifecycle, IEnumerable<IInjectionParameter> parameters,
                           CanResolveBindingDelegate canResolve, bool isSelfBindable)
            : base(services, scopeLifecycle, parameters, canResolve)
        {
            IsSelfBindable = isSelfBindable;
            Validate.ArgumentNotNull(typeTo, "typeTo");
            Validate.ArgumentNotNull(activator, "activator");
            Validate.ArgumentNotNull(constructorResolver, "constructorResolver");
            _typeTo = typeTo;
            _activator = activator;
            _constructorResolver = constructorResolver;
#if NETFX_CORE
            _isGenericTypeDefinition = _typeTo.GetTypeInfo().IsGenericTypeDefinition;
#else
            _isGenericTypeDefinition = _typeTo.IsGenericTypeDefinition;
#endif
            if (typeof(IInitializable).IsAssignableFrom(typeTo))
                ActivatedActions.Add(InjectorUtilsInternal.ActivateInitializable);
            if (typeof(IStartable).IsAssignableFrom(typeTo))
            {
                ActivatedActions.Add(InjectorUtilsInternal.ActivateStartable);
                if (scopeLifecycle != null && scopeLifecycle.ReleaseObjectActions != null &&
                    !scopeLifecycle.ReleaseObjectActions.IsReadOnly)
                    scopeLifecycle.ReleaseObjectActions.Add(InjectorUtilsInternal.DeactivateStartable);
            }
        }

        #endregion

        #region Overrides of BindingBase

        /// <summary>
        /// Resolves instance for the specified <see cref="IBindingContext"/>.
        /// </summary>
        /// <param name="bindingContext">The specified <see cref="IBindingContext"/>.</param>
        /// <returns>An instance of the service.</returns>
        protected override object ResolveService(IBindingContext bindingContext)
        {
            Type typeToActivate = _typeTo;
            if (_isGenericTypeDefinition)
                typeToActivate = TypeTo.MakeGenericType(bindingContext.Service.GetGenericArguments());
            return _activator.Activate(_constructorResolver.Resolve(typeToActivate, bindingContext), bindingContext);
        }

        /// <summary>
        ///     Releases resources held by the object.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing && !IsDisposed)
                _activator.Dispose();
            base.Dispose(disposing);
        }

        #endregion

        #region Properties

        /// <summary>
        ///     Gets <see cref="IActivator" /> for the current binding.
        /// </summary>
        public IActivator Activator
        {
            get { return _activator; }
        }

        /// <summary>
        ///     Gets <see cref="IConstructorResolver" /> for current binding.
        /// </summary>
        public IConstructorResolver ConstructorResolver
        {
            get { return _constructorResolver; }
        }

        /// <summary>
        ///     Gets a type to activate.
        /// </summary>
        public Type TypeTo
        {
            get { return _typeTo; }
        }

        /// <summary>
        /// Indicates that binding was build as self bindable.
        /// </summary>
        public bool IsSelfBindable { get; private set; }

        #endregion

        #region Static method

        /// <summary>
        ///     Creates self bindable <see cref="TypeBinding" />.
        /// </summary>
        internal static TypeBinding CreateSelfBinding(Type service, IActivator activator,
                                                      IConstructorResolver constructorResolver)
        {
            return new TypeBinding(service, activator, constructorResolver, new[] { service },
                                   new TransientScopeLifecycle(), null, BindingBuilderConfigurator.NotKeyedBinding, true);
        }

        #endregion
    }
}